package algebra;

import java.util.HashMap;
import java.util.Map;


import utils.Util;
import utils.exception.ProjectionException;


import entities.concept.Concept;
import entities.vocabulary.Display;
import entities.vocabulary.KBVocabulary;
import entities.vocabulary.QueryKeywords;

public class ProjectionDescription {
	public static final int CONCEPT = 0;
	//public static final int ATOMIC_CONCEPT = 1;
	public static final int FEATURE = 2;
	public static final int CONJUNCTION = 3;
	public static final int ROLE = 4;
	//public static final int UNION = 5;
	

	private static Map<String, Integer> ops = new HashMap<String, Integer>();
	
	static {
		ops.put(QueryKeywords.PD_CONCEPT, CONCEPT);
		//ops.put("up", ATOMIC_CONCEPT);
		ops.put(QueryKeywords.PD_FEATURE, FEATURE);
		ops.put(QueryKeywords.PD_CONJUNCTION, CONJUNCTION);
		ops.put(QueryKeywords.PD_ROLE, ROLE);
		//ops.put("union", UNION);
	}
	
	private String pd;
	private int type = -1;
	private Concept concept; // Concept or atomic concepts. Null otherwise.
	private String arg; // feature name or role name. Null otherwise
	private ProjectionDescription pd1; // nested pds for "role", "join". Null otherwise 
	private ProjectionDescription pd2;
	
	public String getPd() {
		return pd;
	}

	public void setType(int type) {
		this.type = type;
	}

	public void setConcept(Concept concept) {
		this.concept = concept;
	}

	public void setArg(String arg) {
		this.arg = arg;
	}

	public void setPd1(ProjectionDescription pd1) {
		this.pd1 = pd1;
	}

	public void setPd2(ProjectionDescription pd2) {
		this.pd2 = pd2;
	}


	
	public String getArg() {
		return arg;
	}

	public int getType() {
		return type;
	}

	public Concept getConcept() {
		return concept;
	}

	public ProjectionDescription getPd1() {
		return pd1;
	}
	public ProjectionDescription getPd2() {
		return pd2;
	}



	
	public ProjectionDescription(String pd) throws ProjectionException {
		this.pd = pd;
		if(! this.pd.trim().equals("")){
			parse();
		}else{
			//this is a Pd to be parsed by a list of tokens! set values manually!
			concept = null;
			arg = null;
			pd1 = null;
			pd2 = null;
		}
	}
	


	private void parse() throws ProjectionException {
		if (pd == null || pd.trim().equals("")) {
			return;
		}
		pd = pd.trim();
		if (!pd.startsWith(KBVocabulary.LEFTDEL)) {
			throw new ProjectionException("Malformed Projection Description: " + pd);
		}
		
		int space = pd.indexOf(' ');
		String op = pd.substring(1, space);
		if (!ops.containsKey(op)) {
			throw new ProjectionException("Malformed Projection Description: " + pd);
		}
		
		type = ops.get(op);
		switch (type) {
		case ProjectionDescription.CONCEPT:
		//case ProjectionDescription.ATOMIC_CONCEPT:			
			concept = Util.parseConcept(pd.substring(space, pd.length() - 1));
			break;
		
		case ProjectionDescription.FEATURE:	
			arg = pd.substring(space, pd.length() - 1).trim();
			//System.out.println("Feature: " + arg);			
			break;
			
		case ProjectionDescription.CONJUNCTION:
		//case ProjectionDescription.UNION:
			pd = pd.substring(space).trim();
			int idx = findCloseParen(pd);
			//idx += space + 1;
			pd1 = new ProjectionDescription(pd.substring(0, idx));
			pd2 = new ProjectionDescription(pd.substring(idx + 1, pd.length() - 1));
			break;
			
		case ProjectionDescription.ROLE:
			int space2 = pd.indexOf(' ', space + 1);
			arg = pd.substring(space, space2).trim();
			pd1 = new ProjectionDescription(pd.substring(space2, pd.length() - 1));
			//System.out.println("ROLE: " + arg);
			break;
		
		}
	}

	private int findCloseParen(String pd) {
//		pd = pd.trim();
		int open = 0;
		int i = 0;
		for ( ; i < pd.length(); i++) {
			char c = pd.charAt(i);
			if (c == ')') open--;
			else if (c == '(') open++;
			
			if (open == 0) {
				return i + 1;
			}
		}
		return -1;
	}


	@Override
	public String toString() {
		// TODO Auto-generated method stub
		String res = "";
		if(type == ProjectionDescription.CONCEPT){
			res += this.concept+Display.QUERYSYMBOL;
		}else if(type == ProjectionDescription.CONJUNCTION){
			res += "("+this.pd1.toString() +Display.ANDUNICODE+this.pd2.toString()+")";
		}else if(type == ProjectionDescription.FEATURE){
			res += this.arg+Display.QUERYSYMBOL;
		}else if(type == ProjectionDescription.ROLE){
			res += "("+Display.SOMEUNICODE+this.arg+"."+this.pd1+")";
		}
		return res;
	}
	
	
}
